/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.framework;

import org.aopalliance.intercept.Interceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.*;
import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
import org.springframework.lang.Nullable;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@SuppressWarnings("serial")
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {


	@Override
	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config/*代理工厂*/, Method method/*目标方法*/, @Nullable Class<?> targetClass/*目标对象类型*/) {

		/**
		 * 这个接口是切面适配器的注册中心
		 * 1.可以注册AdvisorAdapter 适配器目的：将非advisor类型的增强包装成advisor  ，将advisor类型的增强提取出来对应的 方法拦截器
		 *
		 * */
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		/**
		 * 获取代理工厂内部持有的增强信息
		 * 1. addAdvice
		 * 2. addAdvisor
		 * 最终在代理工厂中都会包装成 advisor
		 * */
		Advisor[] advisors = config.getAdvisors();
		/*创建一个拦截器列表，长度就是advisor的长度*/
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		/*真实的目标对象类型*/
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		/*引介增强相关*/
		Boolean hasIntroductions = null;

		for (Advisor advisor : advisors) {
			/*包含切点信息的增强，内部逻辑就是做匹配算法*/
			if (advisor instanceof PointcutAdvisor) {
				// 转换成 PointcutAdvisor 类型，可以获取到切点信息
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				/*条件二成立，说明当前被代理对象的class匹配当前advisor成功，可能被advisor增强，具体还要看方法匹配。 这里可以看一下 Pointcut 源码*/
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					/*获取切点信息的方法匹配器，做方法级别的匹配*/
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					/*引介相关的，不需要考虑*/
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						/*进行方法匹配 目标方法匹配成功 ， match = true，当前的增强器可以应用到method*/
						match = mm.matches(method, actualClass);
					}
					/*判断是否还需要运行时的匹配*/
					if (match) {
						/*提取出advisor类持有的拦截器信息  registry里面包含三个默认的增强器*/
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							/*如果是运行时匹配，那就走运行时匹配的逻辑*/
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							/*将方法拦截器追加到拦截器列表里面去*/
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			/*不考虑引介，所以直接跳过*/
			else if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
			/*适配所有方法的*/
			else {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}
		/*返回所有匹配当前方法的拦截器*/
		return interceptorList;
	}

	/**
	 * Determine whether the Advisors contain matching introductions.
	 */
	private static boolean hasMatchingIntroductions(Advisor[] advisors, Class<?> actualClass) {
		for (Advisor advisor : advisors) {
			if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (ia.getClassFilter().matches(actualClass)) {
					return true;
				}
			}
		}
		return false;
	}

}
